

#include <asset/assetsystem.h>
#include <asset/reader/assetreaderprogram.h>
#include <fs/fileinfo.h>
#include <gfx/iprogram.h>
#include <gfx/ishader.h>
#include <cffformat.h>
#include <vfs.h>
#include <stdio.h>
#include <string.h>



ceAssetReaderProgram::ceAssetReaderProgram()
{
  CE_OBJECT_CONSTR
}


bool ceAssetReaderProgram::CanRead(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
  if (type != iProgram::AssetTypeID)
    {
      return false;
    }

  ceFileInfo info (locator.GetLocation());
  std::string ext = info.GetExtension();
  return ext == "prog";
}


cffNode* ceAssetReaderProgram::FindNode(cffNode *parent, const char *name)
{
  for (unsigned i=0, j=parent->GetNumberOfNodes(); i<j; ++i)
    {
      cffNode* programNode = parent->GetNode(i);

			if (programNode
					&& programNode->GetName() == std::string("program")
					&& programNode->HasAttribute(0)
          && strcmp (name, (const char*)programNode->GetAttribute(0)) == 0)
        {
          return programNode;
        }

    }

  return 0;
}

ceRef<ceAsset> ceAssetReaderProgram::Read(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
  ceReleasePtr<iFile> file (ceVFS::Get()->Open(locator.GetLocation()));
  if (!file)
    {
      return 0;
    }
	long size = file->GetLength();

  char* buffer = new char[size+1];
  file->Read(buffer, size);
  file->Close ();

  buffer[size] = '\0';

  cffParser parser;
  ceReleasePtr<cffNode> parent (parser.ParseFromBuffer(buffer));
  delete [] buffer;
  if (!parent)
    {
      return 0;
    }


  cffNode* programNode = FindNode(parent, locator.GetName());
  if (!programNode)
    {
      return 0;
    }

  cffNode* techniqueNode = 0;
  for (unsigned i=0, j=programNode->GetNumberOfNodes("technique"); i<j; ++i)
    {
      cffNode* tn = programNode->GetNode("technique", i);
      cffNode* requirementsNode = tn->GetNode ("requirements");
			if (requirementsNode)
        {
          if (!TestRequirements (requirementsNode))
            {
              continue;
            }
        }
      techniqueNode = tn;
    }

	if (!techniqueNode)
    {
      return 0;
    }

  iDevice* device = engine->GetDevice();
  iProgram* program = device->CreateProgram();
  program->SetLocator(locator);

  for (unsigned i=0, j=techniqueNode->GetNumberOfNodes("shader"); i<j; ++i)
    {
      cffNode* shaderNode = techniqueNode->GetNode("shader", i);
			if (shaderNode && shaderNode->HasAttribute(0))
				{
					const char* shaderName = shaderNode->GetAttribute(0);
					iShader * shader = engine->GetAssetSystem()->GetAsset<iShader>(shaderName);
					if (!shader)
						{
							printf ("Shader '%s' could not be loaded\n", shaderName);
							program->Release();
							return 0;
						}
					program->AttachShader(shader);
				}
    }

  if (!program->Link())
    {
      printf ("Program could not be linked\n");
      printf ("%s\n", program->GetLinkInfoLog().c_str());
      program->Release();
      return 0;
    }

  cffNode* bindingsNode = programNode->GetNode("bindings");
	if (bindingsNode)
    {
      for (unsigned i=0, j=bindingsNode->GetNumberOfNodes("binding"); i<j; ++i)
        {
          cffNode* bindingNode = bindingsNode->GetNode("binding", i);
					if (bindingNode
							&& bindingNode->HasNamedAttribute("binding")
							&& bindingNode->HasNamedAttribute("variable"))
						{
							program->RegisterSemantic(ceProgramParameterName ((const char*)bindingNode->GetNamedAttribute("binding")),
																				(const char*)bindingNode->GetNamedAttribute("variable"));
						}
        }
    }



  return program;
}


bool ceAssetReaderProgram::TestRequirements(cffNode *requirementsNode)
{
  return true;
}
